/* multi_wait.c

   Demonstrate the use of wait(2): create multiple children and then wait
   for them all.

   Usage: multi_wait sleep-time...

   One child process is created for each command-line argument. Each child
   sleeps for the number of seconds specified in the corresponding command-line
   argument before exiting. After all children have been created, the parent
   loops, waiting for terminated children, and displaying their PIDs.
*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>

#define BUF_SIZE 1000

/* Return a string containing the current time formatted according to
   the specification in 'format' (see strftime(3) for specifiers).
   If 'format' is NULL, we use "%c" as a specifier (which gives the'
   date and time as for ctime(3), but without the trailing newline).
   Returns NULL on error.
*/
char *currTime(const char *format) {
  static char buf[BUF_SIZE]; /* Nonreentrant */
  time_t t;
  size_t s;
  struct tm *tm;

  t = time(NULL);
  tm = localtime(&t);
  if (tm == NULL) return NULL;

  s = strftime(buf, BUF_SIZE, (format != NULL) ? format : "%c", tm);

  return (s == 0) ? NULL : buf;
}

int main(int argc, char *argv[]) {
  int numDead;    /* Number of children so far waited for */
  pid_t childPid; /* PID of waited for child */
  int j;

  if (argc < 2 || strcmp(argv[1], "--help") == 0) {
    fprintf(stderr, "%s sleep-time...\n", argv[0]);
    exit(EXIT_FAILURE);
  }

  setbuf(stdout, NULL); /* Disable buffering of stdout */

  for (j = 1; j < argc; j++) { /* Create one child for each argument */
    switch (fork()) {
      case -1:
        perror("fork");
        exit(EXIT_FAILURE);

      case 0: /* Child sleeps for a while then exits */
        printf(
            "[%s] child %d started with PID %ld, sleeping %s "
            "seconds\n",
            currTime("%T"), j, (long)getpid(), argv[j]);
        long sleepTime = strtol(argv[j], NULL, 10);
        sleep(sleepTime);
        _exit(EXIT_SUCCESS);

      default: /* Parent just continues around loop */
        break;
    }
  }

  numDead = 0;
  for (;;) { /* Parent waits for each child to exit */
    childPid = wait(NULL);
    if (childPid == -1) {
      if (errno == ECHILD) {
        printf("No more children - bye!\n");
        exit(EXIT_SUCCESS);
      } else { /* Some other (unexpected) error */
        perror("wait");
        exit(EXIT_FAILURE);
      }
    }

    numDead++;
    printf("[%s] wait() returned child PID %ld (numDead=%d)\n", currTime("%T"),
           (long)childPid, numDead);
  }
}